import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.util.Stack;

/**
 * Created by IntelliJ IDEA.
 * User: Administrator
 * Date: Dec 27, 2009
 * Time: 7:06:13 PM
 * To change this template use File | Settings | File Templates.
 */
public class Printer {
    /*input file*/
    private static final String INPUT_FILE_NAME = "input.in";
    /*output file*/
    private static final String OUTPUT_FILE_NAME = "printer.out";
    /*indent*/
    static String INDENT = "  ";

    private static FileWriter fos;
    private static Reader filer;
    private static int currentLevel;
    private static Stack<Node> stack;

    public static void main(String[] args) {
        stack = new Stack<Node>();
        try {
            filer = new FileReader(INPUT_FILE_NAME);
            fos = new FileWriter(OUTPUT_FILE_NAME, true);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = null;
        InputSource is = new InputSource(filer);
        Document document;
        try {
            db = dbf.newDocumentBuilder();
            document = db.parse(is);
            preorderTravers(document); /*traverse root*/
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            fos.write("\r\n");
            fos.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static void preorderTravers(Node node) {
        NodeList list = node.getChildNodes();
        if (node.getNodeType() == Node.ELEMENT_NODE) {
            if (0 < list.getLength()) {
                stack.push(node);
                print(startTag(node));
            } else {
                print(emptyTag(node));
            }
            currentLevel++;
            for (int i = 0; i < list.getLength(); i++) {
                preorderTravers(list.item(i));
            }
            currentLevel--;
            if (!stack.isEmpty()) {
                if (stack.peek().equals(node)) {
                    print(endTag(node));
                    stack.pop();
                }
            }
        } /*else if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
            print(processingInstruction(node));
            for (int i = 0; i < list.getLength(); i++) {
                preorderTravers(list.item(i));
            }
        } else if (node.getNodeType() == Node.TEXT_NODE) {
            print(processingInstruction(node));
            for (int i = 0; i < list.getLength(); i++) {
                preorderTravers(list.item(i));
            }
        } else if (node.getNodeType() == Node.CDATA_SECTION_NODE) {
            print(processingInstruction(node));
            for (int i = 0; i < list.getLength(); i++) {
                preorderTravers(list.item(i));
            }
        } else if (node.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
            print(processingInstruction(node));
            for (int i = 0; i < list.getLength(); i++) {
                preorderTravers(list.item(i));
            }
        } else if (node.getNodeType() == Node.ENTITY_NODE) {
            print(processingInstruction(node));
            for (int i = 0; i < list.getLength(); i++) {
                preorderTravers(list.item(i));
            }
        }*/
        else if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
            print(processingInstruction(node));
            for (int i = 0; i < list.getLength(); i++) {
                preorderTravers(list.item(i));
            }
        } else if (node.getNodeType() == Node.COMMENT_NODE) {
            print(comment(node));
            for (int i = 0; i < list.getLength(); i++) {
                preorderTravers(list.item(i));
            }
        } else if (node.getNodeType() == Node.DOCUMENT_NODE) {
            print(document(node));
            for (int i = 0; i < list.getLength(); i++) {
                preorderTravers(list.item(i));
            }
        }
        /* else if (node.getNodeType() == Node.DOCUMENT_TYPE_NODE) {
            print(processingInstruction(node));
            for (int i = 0; i < list.getLength(); i++) {
                preorderTravers(list.item(i));
            }
        } else if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
            print(processingInstruction(node));
            for (int i = 0; i < list.getLength(); i++) {
                preorderTravers(list.item(i));
            }
        } else if (node.getNodeType() == Node.NOTATION_NODE) {
            print(processingInstruction(node));
            for (int i = 0; i < list.getLength(); i++) {
                preorderTravers(list.item(i));
            }
        }*/
    }

    private static String prefix(Node node) {
        String tag = "";
        tag += startTag(node);
        tag += endTag(node);
        return tag;
    }

    private static String document(Node node) {
        String tag = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
        /*tag += "<?";
        tag += node.getNodeName() + " " + node.getTextContent();
        tag += "/>\n";*/
        return tag;
    }

    private static String comment(Node node) {
        String tag = "";
        tag += "<!--";
        tag += node.getTextContent();

        tag += "-->\n";
        return tag;
    }

    private static String processingInstruction(Node node) {
        String tag = "";
        tag += "<?";
        tag += node.getNodeName() + " " + node.getTextContent();
        tag += "?>\n";
        return tag;
    }

    private static String startTag(Node node) {
        String tag = "";
        tag += "<";
        tag += node.getNodeName();
        if (node.hasAttributes()) {
            for (int i = 0; i < node.getAttributes().getLength(); i++) {
                NamedNodeMap attribMap = node.getAttributes();
                if (3 < node.getAttributes().getLength()) {
                    if (i == 0) {
                        tag += " " + attribMap.item(i) + " ";
                    } else {
                        tag += "\r\n";
                        int spaces = node.getNodeName().length() + 1;
                        for (int k = 0; k < spaces; k++) {
                            tag += " ";
                        }
                        tag += " " + attribMap.item(i) + " ";
                    }
                } else {
                    tag += " " + attribMap.item(i) + " ";
                }
            }
        }
        if (node.getNodeName().contains(":")) {
            tag += ">\r\n";
            if (node.getTextContent().contains("\n")) {
                for (int i = 0; i < node.getTextContent().split("\n").length; i++) {
                    tag += node.getTextContent().split("\n")[i];
                }
                tag += "\n";
            } else {
                tag += "  "+node.getTextContent()+"\n";
            }
        } else {
            tag += ">\n";
        }
        return tag;
    }

    private static String emptyTag(Node node) {
        String tag = "";
        tag += "<";
        tag += node.getNodeName();
        if (node.hasAttributes()) {
            for (int i = 0; i < node.getAttributes().getLength(); i++) {
                NamedNodeMap attribMap = node.getAttributes();
                tag += " " + attribMap.item(i);
            }
        }
        tag += "/>\n";
        return tag;
    }

    private static String endTag(Node node) {
        String tag = "";
        tag += "</";
        tag += node.getNodeName();
        tag += ">\n";
        return tag;
    }

    private static void print(String string) {
        for (int i = 0; i < currentLevel; i++) {
            System.out.print(INDENT);
        }
        System.out.print(string);

        for (int i = 0; i < currentLevel; i++) {
            try {
                fos.write(INDENT);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        try {
            fos.write(string);
            fos.write("\r\n");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
